<!DOCTYPE html>
<html>

<head>
    <title>vectortile test</title>
    <script type="text/javascript" src="https://unpkg.com/axios@0.19.0/dist/axios.min.js"></script>
    <script type="text/javascript" src="https://unpkg.com/dat.gui@0.7.6/build/dat.gui.min.js"></script>
    <link type="text/css" rel="stylesheet" href="https://unpkg.com/maptalks/dist/maptalks.css">
    <script type="text/javascript" src="https://unpkg.com/maptalks/dist/maptalks.js"></script>
    <script type="text/javascript" src="./accesstoken.js"></script>
    <script type="text/javascript" src="https://unpkg.com/three@0.138.0/build/three.min.js"></script>
    <script type="text/javascript" src="https://unpkg.com/maptalks.three@latest/dist/maptalks.three.js"></script>
    <script type="text/javascript" src="./js/mvttogeojson.bundle.js"></script>
    <script type="text/javascript"
        src="https://unpkg.com/three@0.138.0/examples/js/libs/stats.min.js"></script>
    <style>
        html,
        body {
            margin: 0px;
            height: 100%;
            width: 100%;
        }

        #map {
            width: 100%;
            height: 100%;
            background-color: #000;
        }
    </style>
</head>

<body>
    <div id="map"></div>
    <script>

        //custom worker
        const workerKey = 'mvtfetch'
        maptalks.registerWorkerAdapter(workerKey, function (exports, global) {
            //will be called only for once when loaded in worker thread
            exports.initialize = function () {
                console.log('[worker] initialized');
            };
            //to receive message from main thread sent by maptalks.worker.Actor
            exports.onmessage = function (message, postResponse) {
                const data = message.data;
                const { url } = data;
                fetch(url).then(res => res.arrayBuffer()).then(arrayBuffer => {
                    postResponse(null, { data: arrayBuffer }, [arrayBuffer]);
                }).catch(error => {
                    postResponse(null, {}, []);
                });
                //send message back to main thread
                //the parameters:
                //error, data, buffers (arraybuffers in data)
                // postResponse(null, 'message from worker thread', null);
            };
        })
        const actor = new maptalks.worker.Actor(workerKey);
        actor.test = function (params) {
            const { url, key, callback } = params;
            this.send({ url }, null, (err, message) => {
                callback(message);
            });
        }


        var baseLaer = new maptalks.TileLayer('tile', {
            urlTemplate: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png',
            subdomains: ['a', 'b', 'c', 'd'],
            attribution: '&copy; <a href="http://osm.org">OpenStreetMap</a> contributors, &copy; <a href="https://carto.com/">CARTO</a>'
        });
        var map = new maptalks.Map("map", {
            center: [13.416935229170008, 52.529564137540376],
            zoom: 15,
            pitch: 60,
            // pitch: 60,
            // bearing: 180,

            centerCross: true,
            doubleClickZoom: false,
            baseLayer: baseLaer,
            zoomControl: {
                'position': 'bottom-left'
            }
        });

        baseLaer.on('layerload', function () {
            // console.log('layerload');
        })

        // the ThreeLayer to draw buildings
        var threeLayer = new maptalks.ThreeLayer('t', {
            forceRenderOnMoving: true,
            forceRenderOnRotating: true
            // animation: true
        });


        var vectortilelayer;
        threeLayer.prepareToDraw = function (gl, scene, camera) {
            stats = new Stats();
            stats.domElement.style.zIndex = 100;
            document.getElementById('map').appendChild(stats.domElement);

            var light = new THREE.DirectionalLight(0xffffff);
            light.position.set(0, -10, 10).normalize();
            scene.add(light);


            const url = `https://{s}.tiles.mapbox.com/v4/mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v7/{z}/{x}/{y}.vector.pbf?access_token=` + accesstoken;
            vectortilelayer = threeLayer.toThreeVectorTileLayer(url,
                {
                    // minZoom: 6,
                    // maxZoom: 16,
                    debug: true,
                    interactive: false,
                    worker: true,
                    subdomains: ['a', 'b', 'c', 'd'],
                    // tileSize: [512, 512]
                },
                getMaterial);
            // vectortilelayer.debug = true;

            vectortilelayer.onSelectMesh = onSelectMesh;

            //自定义数据访问方法
            vectortilelayer.getTileData = function (q) {
                const { key, url, callback, img } = q;
                const splitstr = key.includes('_') ? '_' : '-';
                let [id, y, x, z] = key.split(splitstr);
                x = parseInt(x);
                y = parseInt(y);
                z = parseInt(z);
                //将数据访问放到worker里，避免阻塞主线程
                actor.test({
                    url,
                    key,
                    callback: function (message) {
                        const { data } = message;
                        if (data) {
                            const geojson = mvt2GeoJSON(data, x, y, z);
                            //避免单位时间内同时callback,尽可能的将callback分开，减少单位时间内的CPU计算
                            setTimeout(() => {
                                callback(key, geojson, img);
                            }, 100 * Math.random());
                        
                        } else {
                            callback(key, null, img);
                        }
                    }
                })
                // axios.get(url, {
                //     timeout: 10000,
                //     responseType: 'arraybuffer'
                // }).then(function (response) {
                //     // console.log(url);
                //     //https://github.com/mapbox/vector-tile-js
                //     const geojson = mvt2GeoJSON(response.data, x, y, z)
                //     // console.log(geojson);
                //     // handle success
                //     callback(key, geojson, img);
                // }).catch(function (error) {
                //     console.error(error);
                //     callback(key, null, img);
                // }).finally(() => {

                // });
            }
            map.addLayer(vectortilelayer);
            animation();
            initGui();


        };
        threeLayer.addTo(map);


        function createLightMateria() {
            const canvasDom = document.createElement('canvas');
            const size = 32;
            canvasDom.width = size;
            canvasDom.height = size;
            const ctx = canvasDom.getContext('2d');
            const gradient = ctx.createRadialGradient(
                canvasDom.width / 2,
                canvasDom.height / 2,
                0,
                canvasDom.width / 2,
                canvasDom.height / 2,
                canvasDom.width / 2);
            gradient.addColorStop(0, 'rgba(255,255,255,1)');
            gradient.addColorStop(0.005, 'rgba(139,69,19,1)');
            gradient.addColorStop(0.4, 'rgba(139,69,19,1)');
            gradient.addColorStop(1, 'rgba(0,0,0,1)');

            ctx.fillStyle = gradient;
            ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2);
            ctx.fill();
            console.log(canvasDom.toDataURL());

            // ctx.fillRect(0, 0, canvasDom.width, canvasDom.height);

            const texture = new THREE.Texture(canvasDom);
            texture.needsUpdate = true; //使用贴图时进行更新
            return texture;
        }

        //高亮材质
        const roadhighMaterial = new THREE.LineBasicMaterial({
            linewidth: 1,
            color: 'yellow',
            // opacity: 0.8,
            transparent: true,
            // dashSize: 0.05,
            // gapSize: 0.05
        });
        const buildhighMaterial = new THREE.MeshBasicMaterial({ color: 'yellow', transparent: true });
        const pointhighMaterial = new THREE.PointsMaterial({
            // size: 10,
            sizeAttenuation: false,
            color: 'yellow',
            // alphaTest: 0.5,
            // vertexColors: THREE.VertexColors,
            //  transparent: true
            // color: 0xffffff,
            size: 10,
            transparent: true, //使材质透明
            // blending: THREE.AdditiveBlending,
            depthTest: true, //深度测试关闭，不消去场景的不可见面
            depthWrite: false,
            //刚刚创建的粒子贴图就在这里用上
        });

        //事件处理
        var selectMesh = [];
        function onSelectMesh(type, e) {
            // console.log(type);
            // console.log(e.target);
            const { target } = e;
            const select = e.selectMesh;
            if (type === 'empty' && selectMesh.length) {
                // if (selectMesh) {
                threeLayer.removeMesh(selectMesh);
                selectMesh = [];
                // }
                return;
            }

            let data, baseObject;
            if (select) {
                data = select.data;
                baseObject = select.baseObject;
                if (baseObject && (!baseLaer.isAdd)) {
                    // console.log(baseObject);
                    // if (selectMesh) {
                    //     threeLayer.removeMesh(selectMesh);
                    // }
                    const meshType = baseObject.getType();
                    if (meshType.includes('Polygon')) {
                        baseObject.setSymbol(buildhighMaterial);
                    } else if (meshType.includes('Line')) {
                        baseObject.setSymbol(roadhighMaterial);
                    } else if (meshType.includes('Point')) {
                        baseObject.setSymbol(pointhighMaterial);
                    }
                    threeLayer.addMesh(baseObject);
                    selectMesh.push(baseObject);
                    // selectMesh = baseObject;
                }
            }
            if (selectMesh.length > 20) {
                threeLayer.removeMesh(selectMesh);
                selectMesh = [];
            }
            // override tooltip
            if (type === 'mousemove' && data) {
                let tooltip = target.getToolTip();
                if (!tooltip) {
                    target.setToolTip(target.getId(), {
                        showTimeout: 0,
                        eventsPropagation: true,
                        dx: 10
                    });
                    tooltip = target.getToolTip();
                }
                const id = baseObject.getId();
                tooltip._content = `id:${id}`;
            }
            //override infowindow
            if (type === 'click' && data) {
                let infoWindow = target.getInfoWindow();
                if (!infoWindow) {
                    target.setInfoWindow({
                        content: '',
                        title: 'message',
                        animationDuration: 0,
                        autoOpenOn: false
                    });
                    infoWindow = target.getInfoWindow();
                }
                const id = baseObject.getId();
                infoWindow.setContent(`id:${id}`);
                if (infoWindow && (!infoWindow._owner)) {
                    infoWindow.addTo(this);
                }
                target.openInfoWindow(e.coordinate);
            }
        }


        const roadMaterial = new THREE.LineBasicMaterial({ color: 'rgb(43,69,107)', transparent: true, opacity: 1 });
        const waterMaterial = new THREE.MeshBasicMaterial({ color: 'green', transparent: true });
        const waterwayMaterial = new THREE.LineBasicMaterial({ color: 'red', transparent: true, opacity: 1 });
        const buildMaterial = new THREE.MeshPhongMaterial({ color: '#fff', transparent: true });
        const pointMaterial = new THREE.PointsMaterial({
            // size: 10,
            sizeAttenuation: false,
            // color: fillStyle,
            // alphaTest: 0.5,
            // vertexColors: THREE.VertexColors,
            //  transparent: true
            // color: 0xffffff,
            size: 5,
            transparent: true, //使材质透明
            // blending: THREE.AdditiveBlending,
            depthTest: true, //深度测试关闭，不消去场景的不可见面
            depthWrite: false,
            map: createLightMateria()
            //刚刚创建的粒子贴图就在这里用上
        });

        function getMaterial(layerName, data, key, geojson) {
            // console.log(layerName, data);
            switch (layerName) {
                // case 'water': {
                //     return waterMaterial;
                // }
                case 'waterway': {
                    return waterwayMaterial;
                }
                case 'road': {
                    // console.log(data[0]);
                    const type = data[0].geometry.type;
                    if (type === 'LineString') {
                        return roadMaterial;
                    } else if (type === 'Polygon') {
                        // return waterMaterial;
                    }
                }
                case 'building': {
                    return buildMaterial;
                }
                // case 'poi_label': {
                //     return pointMaterial;
                // }

                default: {
                    // return null;
                }
            }

        }


        function animation() {
            // layer animation support Skipping frames
            if ((!map.isMoving()) && (!map.isZooming()) && (!map.isRotating()) && (!params.rotate)) {
                threeLayer._needsUpdate = !threeLayer._needsUpdate;
                if (threeLayer._needsUpdate) {
                    threeLayer.redraw();
                }
            }
            stats.update();
            requestAnimationFrame(animation);
            if (params.rotate) {
                map.setBearing(map.getBearing() + 0.3);
            }


        }


        var params = {
            add: true,
            buildingColor: buildMaterial.color.getStyle(),
            buildingOpacity: 1,
            animateShow: animateShow,

            waterColor: waterMaterial.color.getStyle(),
            waterOpacity: 1,

            roadColor: roadMaterial.color.getStyle(),
            roadOpacity: 1,

            pointSize: pointMaterial.size,
            blending: false,

            show: true,
            altitude: 0,
            rotate: false,
            interactive: false
        };

        function initGui() {

            var gui = new dat.GUI();
            gui.add(params, 'add').onChange(function () {
                if (params.add) {
                    map.addLayer(vectortilelayer);
                } else {
                    map.removeLayer(vectortilelayer);
                }
            });
            gui.addColor(params, 'buildingColor').name('buildingColor').onChange(function () {
                buildMaterial.color.set(params.buildingColor);
            });
            gui.add(params, 'buildingOpacity', 0, 1).onChange(function () {
                buildMaterial.opacity = params.buildingOpacity;
            });


            gui.addColor(params, 'waterColor').name('waterColor').onChange(function () {
                waterMaterial.color.set(params.waterColor);
            });
            gui.add(params, 'waterOpacity', 0, 1).onChange(function () {
                waterMaterial.opacity = params.waterOpacity;
            });


            gui.addColor(params, 'roadColor').name('roadColor').onChange(function () {
                roadMaterial.color.set(params.roadColor);
            });
            gui.add(params, 'roadOpacity', 0, 1).onChange(function () {
                roadMaterial.opacity = params.roadOpacity;

            });


            gui.add(params, 'blending').onChange(function () {
                if (params.blending) {
                    pointMaterial.blending = THREE.AdditiveBlending;
                } else {
                    pointMaterial.blending = THREE.NoBlending;
                }
            });
            gui.add(params, 'pointSize', 2, 20).onChange(function () {
                pointMaterial.size = params.pointSize;
            });



            gui.add(params, 'show').onChange(function () {
                if (params.show) {
                    vectortilelayer.show();
                } else {
                    vectortilelayer.hide();
                }
            });
            gui.add(params, 'altitude', 0, 300).onChange(function () {
                const meshs = vectortilelayer.getBaseObjects();
                meshs.forEach(function (mesh) {
                    mesh.setAltitude(params.altitude);
                });
            });
            gui.add(params, 'animateShow');
            gui.add(params, 'rotate');
            gui.add(params, 'interactive').onChange(function () {
                vectortilelayer._opts.interactive = params.interactive;
                const meshs = vectortilelayer.getBaseObjects();
                meshs.forEach(function (mesh) {
                    mesh.options.interactive = params.interactive;
                });
            });
        }

        function animateShow() {
            const meshs = vectortilelayer.getBaseObjects();
            meshs.forEach(function (mesh) {
                mesh.animateShow({
                    duration: 1300
                });
            });
        }


    </script>
</body>

</html>